import Creater from '../types/creater';
import { DirectionStr, FocusEventName, FocusKeyMaysKeys, FocusKeyMaysProps, OptionType } from '../types/focus';
import Views from '../types/views';
import { isNumber, hasOwn } from './valid';

/**
 * 焦点算法
 */
export default class Calc {
  // 锁定确认键响应
  protected blockEnter: boolean = false;
  // 兼容距离计算
  private failoverDist: number;
  // 是否响应keyup按键
  private _useKeyUp: boolean;
  // 按键方向
  private _direction: string = "";
  // 按键响应map
  private keyMaps: {
    [key in FocusKeyMaysKeys]: number[];
  } = {
      LEFT: [3, 37],
      UP: [1, 38, 61],
      RIGHT: [4, 39],
      DOWN: [2, 40, 77],
      ENTER: [13],
      BACK: [8, 22, 27, 340, 461, 10009],
      NUMBER: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105],
      FOWARD: [70, 417],
      REWIND: [82, 412],
      PLAY: [80, 415],
      PAUSE: [81, 19],
      STOP: [83, 413],
      DELETE: [46, 67]
    };

  protected constructor(options?: OptionType) {
    this.failoverDist = options?.failoverDist || 0
    this._useKeyUp = options?.useKeyUp || false
    this.cnkiMaps(options?.keyMaps || {})
  }

  // 获取是否开启keyup响应
  get useKeyUp() {
    return this._useKeyUp
  }

  // 获取按键方向
  get direction() {
    return this._direction;
  }

  get enterKeyCode() {
    return this.keyMaps.ENTER[0]
  }

  /**
   * 计算焦点元素到某点的最短距离
   * @param left 焦点left
   * @param top 焦点top
   * @param width 焦点width
   * @param height 焦点height
   * @param x 基准点x
   * @param y 基准点y
   * @returns number
   */
  protected rulesSurfaceToPoint(left: number, top: number, width: number, height: number, x: number, y: number): number {
    if (x < left) {
      if (y < top) {
        return Math.sqrt(Math.pow(left - x, 2) + Math.pow(top - y, 2));
      } else if (y >= top && y <= top + height) {
        return left - x;
      } else {
        return Math.sqrt(Math.pow(left - x, 2) + Math.pow(y - top - height, 2));
      }
    } else if (x > left + width) {
      if (y < top) {
        return Math.sqrt(Math.pow(x - left - width, 2) + Math.pow(top - y, 2));
      } else if (y >= top && y <= top + height) {
        return x - left;
      } else {
        return Math.sqrt(Math.pow(x - left - width, 2) + Math.pow(y - top - height, 2));
      }
    } else {
      if (y < top) {
        return top - y;
      } else if (y >= top && y <= top + height) {
        return 0;
      } else {
        return y - top - height;
      }
    }
  }

  /**
   * 判断元素某一方向上是否交错
   * 参数取两个元素同一方向上的起点及相应尺寸
   * @param pb prev元素起点
   * @param pc prev元素相应方向的尺寸
   * @param nb next元素起点
   * @param nc next元素相应方向的尺寸
   * @returns number
   */
  protected getOverlay(pb: number, pc: number, nb: number, nc: number): number {
    if (nb >= pb + pc || nb + nc <= pb) {
      return 0;
    } else {
      let nd = nb + nc;
      let pd = pb + pc;
      if (nd > pd) {
        return Math.min(pd - nb, pc);
      } else if (nd === pd) {
        return Math.min(nc, pc);
      } else {
        return Math.min(nd - pb, nc);
      }
    }
  }

  /**
   * 对比返回距离x,y坐标更近的焦点
   * @param x 基准点x
   * @param y 基准点y
   * @param target 下一个焦点
   * @param cache 缓存焦点对象
   * @returns Creater | undefined
   */
  protected rulesPoint(x: number, y: number, target: Creater, cache?: Creater): Creater | undefined {
    if (cache) {
      let { posAndSize } = target;
      let { top: nt, left: nl, width: nw, height: nh } = posAndSize;
      if (nw && nh) {
        let { top, left, width, height } = cache;
        let nextDist = this.rulesSurfaceToPoint(nl, nt, nw, nh, x, y);
        let prevDist = this.rulesSurfaceToPoint(left, top, width, height, x, y);
        if (nextDist < prevDist) {
          return target;
        } else if (nextDist === prevDist) {
          if (nt > top || nl > left) {
            return target;
          } else {
            return cache;
          }
        } else {
          return cache;
        }
      } else {
        return cache;
      }
    } else {
      return target;
    }
  }

  /**
   * 通用元素焦点移动计算规则
   * @param point 焦点元素对象
   * @param target 待做对比的焦点元素对象
   * @param direction 焦点移动方向
   * @param cache 满足条件的最优的焦点元素对象
   * @returns Creater | undefined
   */
  protected rules(point: Creater, target: Creater, direction: DirectionStr, cache?: Creater): Creater | undefined {
    let { posAndSize: nextPosAndSize, name, sign, __pools__ } = target;
    let { left: nextLeft, top: nextTop, width: nextWidth, height: nextHeight } = nextPosAndSize;
    if (!nextWidth || !nextHeight) {
      return cache;
    }
    if (name === 'group') {
      // 检测该target为group类型时；如果里边没有节点列表，则跳过
      const list = __pools__?.getInnerGroupList(sign).length;
      if (!list) {
        return cache;
      }
    }
    let temp: Creater | undefined;
    let { posAndSize } = point;
    let { top, left, width, height } = posAndSize;
    if (direction === 'left') {
      // left值小于焦点元素, 且left、top差值均最小, 且水平方向有交集
      let nextOverlay = this.getOverlay(top, height, nextTop, nextHeight);
      if (nextLeft + nextWidth <= left && nextOverlay) {
        if (cache) {
          let { posAndSize: prevPosAndSize } = cache;
          let { left: prevLeft, top: prevTop, height: prevHeight } = prevPosAndSize;
          if (prevLeft > nextLeft) {
            temp = cache;
          } else if (prevLeft === nextLeft) {
            let prevOverlay = this.getOverlay(top, height, prevTop, prevHeight);
            if (prevOverlay >= nextOverlay) {
              temp = cache;
            } else {
              temp = target;
            }
          } else {
            temp = target;
          }
        } else {
          temp = target;
        }
      }
    } else if (direction === 'right') {
      // left值大于焦点元素, 且left、top差值均最小, 且水平方向有交集
      let nextOverlay = this.getOverlay(top, height, nextTop, nextHeight);
      if (nextLeft >= left + width && nextOverlay) {
        if (cache) {
          let { posAndSize: prevPosAndSize } = cache;
          let { left: prevLeft, top: prevTop, height: prevHeight } = prevPosAndSize;
          if (prevLeft < nextLeft) {
            temp = cache;
          } else if (prevLeft === nextLeft) {
            let prevOverlay = this.getOverlay(top, height, prevTop, prevHeight);
            if (prevOverlay >= nextOverlay) {
              temp = cache;
            } else {
              temp = target;
            }
          } else {
            temp = target;
          }
        } else {
          temp = target;
        }
      }
    } else if (direction === 'up') {
      // top值小于焦点元素, 水平方向无交集
      let targetPosition = nextTop + nextHeight;
      if (targetPosition <= top) {
        if (cache) {
          let { posAndSize: prevPosAndSize } = cache;
          let { left: prevLeft, top: prevTop, width: prevWidth, height: prevHeight } = prevPosAndSize;
          let cachePosition = prevTop + prevHeight;
          let nextOverlay = this.getOverlay(left, width, nextLeft, nextWidth);
          let prevOverlay = this.getOverlay(left, width, prevLeft, prevWidth);
          if (nextOverlay > prevOverlay) {
            if (targetPosition >= cachePosition) {
              temp = target;
            } else {
              if (top - targetPosition <= this.failoverDist) {
                temp = target;
              } else {
                temp = cache;
              }
            }
          } else if (nextOverlay === prevOverlay) {
            if (cachePosition > targetPosition) {
              temp = cache;
            } else if (cachePosition === targetPosition) {
              if (nextOverlay === 0) {
                let prevDist = this.rulesSurfaceToPoint(prevLeft, prevTop, prevWidth, prevHeight, prevLeft < left ? left : left + width, top);
                let nextDist = this.rulesSurfaceToPoint(nextLeft, nextTop, nextWidth, nextHeight, nextLeft < left ? left : left + width, top);
                if (prevDist >= nextDist) {
                  temp = target;
                } else {
                  temp = cache;
                }
              } else {
                if (prevLeft > nextLeft) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else {
            if (cachePosition >= targetPosition) {
              temp = cache;
            } else {
              if (top - cachePosition <= this.failoverDist) {
                temp = cache;
              } else {
                temp = target;
              }
            }
          }
        } else {
          temp = target;
        }
      }
    } else if (direction === 'down') {
      // top值大于焦点元素, 水平方向无交集
      if (nextTop >= top + height) {
        if (cache) {
          let { posAndSize: prevPosAndSize } = cache;
          let { left: prevLeft, top: prevTop, width: prevWidth, height: prevHeight } = prevPosAndSize;
          let nextOverlay = this.getOverlay(left, width, nextLeft, nextWidth);
          let prevOverlay = this.getOverlay(left, width, prevLeft, prevWidth);
          if (nextOverlay > prevOverlay) {
            // target交集更大
            if (nextTop > prevTop) {
              if (nextTop - top - height <= this.failoverDist) {
                temp = target;
              } else {
                if (nextTop < prevTop + prevHeight) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else if (nextOverlay === prevOverlay) {
            if (nextTop > prevTop) {
              temp = cache;
            } else if (nextTop === prevTop) {
              if (nextOverlay === 0) {
                let prevDist = this.rulesSurfaceToPoint(
                  prevLeft,
                  prevTop,
                  prevWidth,
                  prevHeight,
                  prevLeft < left ? left : left + width,
                  top + height
                );
                let nextDist = this.rulesSurfaceToPoint(
                  nextLeft,
                  nextTop,
                  nextWidth,
                  nextHeight,
                  nextLeft < left ? left : left + width,
                  top + height
                );
                if (prevDist >= nextDist) {
                  temp = target;
                } else {
                  temp = cache;
                }
              } else {
                if (prevLeft > nextLeft) {
                  temp = target;
                } else {
                  temp = cache;
                }
              }
            } else {
              temp = target;
            }
          } else {
            // cache交集更大
            if (prevTop > nextTop) {
              if (prevTop - top - height <= this.failoverDist) {
                temp = cache;
              } else {
                if (prevTop < nextTop + nextHeight) {
                  temp = cache;
                } else {
                  temp = target;
                }
              }
            } else {
              temp = cache;
            }
          }
        } else {
          temp = target;
        }
      }
    }
    return temp;
  }

  /**
   * 移入Views或group计算规则
   * @param pointer 区域限制对象
   * @param target 待做对比的焦点对象
   * @param direction 焦点移动方向
   * @param cache 满足条件的最优的焦点元素对象
   * @returns Creater | undefined
   */
  protected rulesDist(pointer: Creater, target: Creater, direction: DirectionStr = 'down', cache?: Creater): Creater | undefined {
    let { posAndSize: nextPosAndSize, name, sign, __pools__ } = target;
    let { left: nextLeft, top: nextTop, width: nextWidth, height: nextHeight } = nextPosAndSize;
    if (!nextWidth && !nextHeight) {
      return cache;
    }
    if (name === 'group') {
      // 检测该target为group类型时；如果里边没有节点列表，则跳过
      const list = __pools__?.getInnerGroupList(sign).length;
      if (!list) {
        return cache;
      }
    }
    let { posAndSize } = pointer;
    let { left, top, width, height } = posAndSize;
    if (nextLeft >= left && nextLeft + nextWidth <= left + width && nextTop >= top && nextTop + nextHeight <= top + height) {
      let temp: Creater | undefined;
      if (cache) {
        let { posAndSize: prevPosAndSize } = cache;
        let { left: prevLeft, top: prevTop, height: prevHeight } = prevPosAndSize;
        if (direction === 'left') {
          if (nextTop < prevTop || (nextTop === prevTop && nextLeft > prevLeft)) {
            temp = target;
          } else {
            temp = cache;
          }
        } else if (direction === 'right' || direction === 'down') {
          if (nextTop < prevTop || (nextTop === prevTop && nextLeft < prevLeft)) {
            temp = target;
          } else {
            temp = cache;
          }
        } else if (direction === 'up') {
          if (nextTop + nextHeight > prevTop + prevHeight || (nextTop + nextHeight === prevTop + prevHeight && nextLeft < prevLeft)) {
            temp = target;
          } else {
            temp = cache;
          }
        }
      } else {
        temp = target;
      }
      return temp;
    } else {
      return cache;
    }
  }

  /**
   * 移入新group的焦点计算方法
   * @param direction 移动方向
   * @param pointer 焦点元素对象
   * @returns Creater | undefined
   */
  protected rulesInToGroup(direction: DirectionStr, pointer: Creater): Creater | undefined {
    let temp: Creater | undefined;
    let { __pools__, sign } = pointer;
    let list = __pools__?.getInnerGroupList(sign) || [];
    if (list.length) {
      list.forEach((val) => {
        temp = this.rulesDist(pointer, val, direction, temp);
      });
      if (temp?.name === 'group') {
        return this.rulesInToGroup(direction, temp);
      }
    }
    return temp;
  }

  /**
   * 移入新Views的焦点计算方法
   * @param direction 方向
   * @param views 对应Views对象
   * @param popSign 弹层标记
   * @returns Creater | undefined
   */
  protected rulesInToViews(direction: DirectionStr, views: Views, popSign: string): Creater | undefined {
    let temp: Creater | undefined;
    let { viewSign, routeViews, __baseView__ } = views;
    let childViews: Creater[] = []
    if (viewSign && routeViews[viewSign]) {
      for (const val of Object.values(routeViews[viewSign])) {
        val.__baseView__ && childViews.push(val.__baseView__)
      }
    }
    let tempArr = (views.getLayer(popSign)?.items || []).concat(childViews);
    if (tempArr.length && __baseView__) {
      tempArr.forEach((val) => {
        temp = this.rulesDist(__baseView__, val, direction, temp);
      });
      if (temp?.name === 'views') {
        return this.rulesInToViews(direction, temp?.__views__!, popSign);
      } else if (temp?.name === 'group') {
        return this.rulesInToGroup(direction, temp);
      }
    }
    return temp;
  }

  /**
   * 查找相邻节点规则,如果含有group标记，则仅在对应group内查找相邻节点
   * @param direction 移动方向
   * @param pointer 焦点元素对象
   * @param inGroup 是否限定在路由区域内移动
   * @param popSign 弹层标记
   * @returns Creater | undefined
   */
  protected rulesInArea(direction: DirectionStr, pointer: Creater, inGroup: boolean = false, popSign: string = ""): Creater | undefined {
    let next: Creater | undefined;
    let { __views__, __group__, name } = pointer;
    let { viewSign, routeViews, parent } = __views__!;
    let parentList: Creater[] = []
    if (name === 'views') {
      let layerList: Creater[] = [];
      if (parent !== undefined) {
        if (parent.viewSign && parent.routeViews[parent.viewSign]) {
          for (const val of Object.values(parent.routeViews[parent.viewSign])) {
            val.__baseView__ && layerList.push(val.__baseView__)
          }
        }
      }
      parentList = layerList.concat(parent?.getLayer(popSign)?.items || [])
    } else {
      // 获取焦点子view代理对象
      let childViews: Creater[] = []
      if (viewSign && routeViews[viewSign]) {
        for (const val of Object.values(routeViews[viewSign])) {
          val.__baseView__ && childViews.push(val.__baseView__)
        }
      }
      parentList = (__views__?.getLayer(popSign)?.items || []).concat(childViews);
    }
    if (parentList.length) {
      parentList.forEach((value) => {
        if (value !== pointer) {
          next = this.rules(pointer, value, direction, next) || next;
        }
      });
      if (next !== undefined) {
        if (next.name === 'views') {
          if (!inGroup) {
            // 移入进子路由中，移动到对应方向上的第一个可见节点上
            return this.rulesInToViews(direction, next.__views__!, popSign);
          }
        } else if (next.name === 'group') {
          return this.rulesInToGroup(direction, next);
        } else if (next.name === 'items') {
          return next;
        }
      } else if (!inGroup) {
        // 移出区域
        let innerParent, viewCreater, innerView;
        if (name === 'views') {
          innerView = __views__?.parent
        } else {
          innerView = __views__
        }
        innerParent = innerView?.parent
        viewCreater = innerView?.__baseView__
        if (__group__) {
          return this.rulesInArea(direction, __group__, inGroup, popSign);
        } else if (innerParent) {
          return this.rulesInArea(direction, viewCreater!, inGroup, popSign);
        }
      }
    }
    return next;
  }

  /**
   * 查找指定方向上与pointer含有相同标记的下一个焦点
   * @param direction 方向
   * @param pointer 焦点对象
   * @returns Creater | undefined
   */
  protected rulesInSign(direction: DirectionStr, pointer: Creater): Creater | undefined {
    let temp: Creater | undefined;
    if (pointer.__sign__?.length) {
      pointer.__sign__?.forEach((val) => {
        val.selected = false;
        if (val != pointer) {
          temp = this.rules(pointer, val, direction, temp) || temp;
        }
      });
      if (temp) {
        (temp as Creater).selected = true;
      } else {
        pointer.selected = true;
      }
    }
    return temp;
  }

  /**
   * 换到下一行第一个
   * @param pointer 焦点对象
   * @param popSign 弹层标记
   * @param inGroup 是否限定区域
   * @returns Creater | undefined
   */
  protected rulesTurnDown(pointer: Creater, popSign: string, inGroup: boolean = false): Creater | undefined {
    let { posAndSize } = pointer;
    let { top, height } = posAndSize;
    let temper: Creater | undefined = this.rulesInArea('down', pointer, inGroup, popSign);
    if (temper) {
      let tempObj: Creater | undefined,
        flag: boolean = false;
      do {
        tempObj = this.rulesInArea('left', temper, inGroup, popSign);
        if (tempObj) {
          let { posAndSize } = tempObj;
          let { top: nextTop } = posAndSize;
          flag = nextTop >= top + height;
        } else {
          flag = false;
        }
      } while (tempObj && flag && (temper = tempObj));
    }
    return temper;
  }

  /**
   * 换到上一行最后一个
   * @param pointer 焦点对象
   * @param popSign 弹层标记
   * @param inGroup 是否限定区域
   * @returns Creater | undefined
   */
  protected rulesTurnUp(pointer: Creater, popSign: string, inGroup: boolean = false): Creater | undefined {
    let { posAndSize } = pointer;
    let { top } = posAndSize;
    let temper: Creater | undefined = this.rulesInArea('up', pointer, inGroup, popSign);
    if (temper) {
      let tempObj: Creater | undefined,
        flag = false;
      do {
        tempObj = this.rulesInArea('right', temper, inGroup, popSign);
        if (tempObj) {
          let { posAndSize } = tempObj;
          let { top: nextTop, height: nextHeight } = posAndSize;
          flag = nextTop + nextHeight <= top;
        } else {
          flag = false;
        }
      } while (tempObj && flag && (temper = tempObj));
    }
    return temper;
  }

  /**
   * 从列表中查找距离基准坐标最近的焦点
   * @param x 基准点X坐标
   * @param y 基准点Y坐标
   * @param list 焦点列表
   * @param popSign 弹层标记
   * @returns Creater | undefined
   */
  protected rulesPosition(x: number, y: number, list: Creater[], popSign: string): Creater | undefined {
    let temp: Creater | undefined;
    if (list.length) {
      list.forEach((val) => {
        temp = this.rulesPoint(x, y, val, temp);
      });
      let { __views__ } = temp!;
      // 获取焦点子view代理对象
      let { viewSign, routeViews } = __views__!;
      if (temp?.name === 'views') {
        let childViews: Creater[] = []
        if (viewSign && routeViews[viewSign]) {
          for (const val of Object.values(routeViews[viewSign])) {
            val.__baseView__ && childViews.push(val.__baseView__)
          }
        }
        let tempArr = (__views__?.getLayer(popSign)?.items || []).concat(childViews);
        return this.rulesPosition(x, y, tempArr, popSign);
      } else if (temp?.name === 'group') {
        return this.rulesPosition(x, y, __views__?.getLayer(popSign)?.getInnerGroupList(temp?.sign) || [], popSign);
      }
    }
    return temp;
  }

  /**
   * 从列表中查找满足条件的焦点
   * @param list 指定焦点列表
   * @param popSign 弹层标记
   * @param callBack 对比条件
   * @returns Creater | undefined
   */
  protected ergodicList(list: Creater[], popSign: string, callBack: (pointer: Creater) => boolean): Creater | undefined {
    let temp: Creater | undefined;
    for (let i = 0; i < list.length; i++) {
      const item = list[i];
      let { __views__, sign, name } = item;
      // 获取焦点子view代理对象
      let { viewSign, routeViews } = __views__!;
      if (name === 'views') {
        let childViews: Creater[] = []
        if (viewSign && routeViews[viewSign]) {
          for (const val of Object.values(routeViews[viewSign])) {
            val.__baseView__ && childViews.push(val.__baseView__)
          }
        }
        let tempArr = (__views__?.getLayer(popSign)?.items || []).concat(childViews);
        temp = this.ergodicList(tempArr, popSign, callBack);
      } else if (name === 'group') {
        temp = this.ergodicList(__views__?.getLayer(popSign)?.getInnerGroupList(sign) || [], popSign, callBack);
      } else if (name === 'items') {
        if (callBack(item)) {
          temp = item;
        }
      }
      if (temp) {
        break;
      }
    }
    return temp;
  }

  /**
   * 变更默认按键值对应关系
   * @param options 自定义按键值
   */
  private cnkiMaps(options: {
    [key in FocusKeyMaysProps]?: number | number[];
  }) {
    for (let [mapKey, mapVal] of Object.entries(options)) {
      let cnkiKey = mapKey.toUpperCase();
      if (hasOwn(this.keyMaps, cnkiKey)) {
        let mapList: number[];
        if (isNumber(mapVal)) {
          mapList = [mapVal as number];
        } else if (mapVal instanceof Array) {
          mapList = mapVal;
        } else {
          continue;
        }
        mapList.forEach((val: number) => {
          for (let [key, list] of Object.entries(this.keyMaps)) {
            let index = list.indexOf(val);
            let prop = key as FocusKeyMaysKeys;
            if (prop === cnkiKey && index < 0) {
              this.keyMaps[prop].push(val);
            } else if (prop !== cnkiKey && index >= 0) {
              this.keyMaps[prop].splice(index, 1);
            }
          }
        });
      }
    }
  }

  /**
   * 获取事件名称
   * @param keyAction 按键行为
   * @param longFlag 是否为长按
   * @returns eventName
   */
  protected getEventName(keyAction: FocusKeyMaysKeys, longFlag?: boolean): FocusEventName | "" {
    this._direction = ["LEFT", "RIGHT", "UP", "DOWN"].includes(keyAction) ? keyAction.toLocaleLowerCase() : ""
    if (keyAction === 'ENTER') {
      if (this.blockEnter) {
        this.blockEnter = false
        return ""
      } else {
        this.blockEnter = true
      }
    } else {
      this.blockEnter = false
    }
    return 'key' + (longFlag ? 'Long' : '') + keyAction.toLowerCase().replace(/^([a-z])(.+)/, (match, a, b) => a.toUpperCase() + b) as FocusEventName
  }

  /**
   * 获取按键行为
   * @param code 按键Code
   * @returns 按键事件
   */
  protected getKeyAction(code: number): FocusKeyMaysKeys | undefined {
    for (const [key, value] of Object.entries(this.keyMaps)) {
      if (value.includes(code)) {
        return key as FocusKeyMaysKeys
      }
    }
  }
}
